
#include <BKeyEvent>
#include <BMouseEvent>
#include <BCamera>
#include <BViewer>
#include <BRender>
#include <BPainter>
#include <BFont>
#include <BScene>
#include <BSystem>

#include <BSphereHandler>
#include "member_BSphereHandler.h"

using namespace BWE;

#define member					(*(member_BSphereHandler*)_ptr)
#define member_allocate()		_ptr = new member_BSphereHandler(this)
#define member_release()		delete (member_BSphereHandler*)_ptr

BSphereHandler::BSphereHandler(BHandler* parent) : BHandler(parent)
{
	member_allocate();
}
BSphereHandler::~BSphereHandler()
{
	member_release();
}

void BSphereHandler::focus(const BObject* object)
{
	BSpace space;
	if (const BScene* scene = dynamic_cast<const BScene*>(object))
		space = scene->space();
	else if (const BGroup* group = dynamic_cast<const BGroup*>(object))
		space = group->space();
	else if (const BNode* node = dynamic_cast<const BNode*>(object))
		space = node->space();
	else if (const BShape* shape = dynamic_cast<const BShape*>(object))
		space = shape->space();
	else if (const BScene* scene = this->viewer()->scene())
		space = scene->space();
	focus(space);
}
void BSphereHandler::focus(const BSpace& space)
{
	BCamera* camera = this->camera();
	if (!camera)
		return;

	BReal radius = space.radius();
	if (bZero(radius))
		radius = 1;
	BVector center = space.center();
	BReal fovy = camera->fovy() * 0.5;
	fovy *= PI / 180;;
	BReal dist = radius / tan(fovy);
	BVector vec(0, -1, 1);
	vec.normalize();
	BVector position = center + vec * dist;
	camera->setPosition(position);
	camera->setCenter(center);

	double far = space.radius() * 10;
	if (camera->far() < far)
	{
		camera->setFar(far);
	}
	BReal near = space.radius() * 0.01;
	if (camera->near() > near)
	{
		camera->setNear(near);
	}

	this->fresh();
}

void BSphereHandler::setMoveStep(BReal moveStep)
{
	member.moveStep = moveStep;
}
BReal BSphereHandler::moveStep() const
{
	return member.moveStep;
}

void BSphereHandler::setRotateStep(BReal rotateStep)
{
	member.rotateStep = rotateStep;
}
BReal BSphereHandler::rotateStep() const
{
	return member.rotateStep;
}

bool BSphereHandler::keyDownEvent(const BKeyEvent& keyEvent)
{
	Key key = keyEvent.key();
	BCamera* camera = this->camera();
	if (camera && key == Key_Space)
	{
		this->focus();
		return true;
	}
	return false;
}
bool BSphereHandler::keyUpEvent(const BKeyEvent& keyEvent)
{
	Key key = keyEvent.key();
	return false;
}

bool BSphereHandler::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	member.rightPressed = false;
	member.middlePressed = false;
	return false;
}
bool BSphereHandler::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	member.rightPressed = false;
	member.middlePressed = false;
	return false;
}

bool BSphereHandler::mousePressEvent(const BMouseEvent& mouseEvent)
{
	Button button = mouseEvent.button();
	BPoint pos = mouseEvent.pos();
	if (button == Button_Right)
	{
		member.rightPressed = true;
		member.rightPressedPos = pos;
		member.rightPressedDirect = this->camera()->center() - this->camera()->position();
		return true;
	}
	if (button == Button_Middle)
	{
		member.middlePressed = true;
		member.middlePressedPos = pos;
		member.middlePressedPosition = this->camera()->position();
		member.middlePressedCenter = this->camera()->center();
		return true;
	}
	return false;
}
bool BSphereHandler::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	Button button = mouseEvent.button();
	BPoint pos = mouseEvent.pos();
	if (button == Button_Right)
	{
		member.rightPressed = false;
	}
	if (button == Button_Middle)
	{
		member.middlePressed = false;
	}
	return false;
}
bool BSphereHandler::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	Button button = mouseEvent.button();
	BPoint pos = mouseEvent.pos();
	if (member.rightPressed)
	{
		if (member.rightPressedPos == pos)
			return false;

		BCamera* camera = this->camera();
		BVector center = camera->center();
		BVector up = camera->up();
		up.normalize();

		BVector direct = member.rightPressedDirect;
		BVector vec = -direct;
		BReal length = direct.length();

		BVector right = direct.cross(up);
		right.normalize();

		up = right.cross(direct);
		up.normalize();

		BReal rotateStep = PI / 180 * member.rotateStep;
		BPoint offset = pos - member.rightPressedPos;
		BQuater hquat(-up, rotateStep * offset.x());
		vec *= hquat;
		BQuater vquat(-right, rotateStep * offset.y());
		vec *= vquat;
		camera->setPosition(center + vec);
		camera->setCenter(center);
		if (offset.length() > 100)
		{
			member.rightPressedPos = pos;
			member.rightPressedDirect = this->camera()->center() - this->camera()->position();
		}
		this->fresh();
		return true;
	}
	if (member.middlePressed && member.middlePressedPos != pos)
	{
		BViewer* viewer = this->viewer();
		BCamera* camera = this->camera();
		BVector position = member.middlePressedPosition;
		BVector center = member.middlePressedCenter;
		BVector direct = center - position;
		BReal length = direct.length();
		if (camera->ortho())
		{
			length = camera->near();
		}
		BVector v0 = viewer->vertex(pos, length);
		BVector v1 = viewer->vertex(member.middlePressedPos, length);
		camera->setPosition(position + (v1 - v0));
		camera->setCenter(center + (v1 - v0));

		BPoint offset = pos - member.middlePressedPos;
		if (offset.length() > 100)
		{
			member.middlePressedPos = pos;
			member.middlePressedPosition = camera->position();
			member.middlePressedCenter = camera->center();
		}
		viewer->fresh();
		return true;
	}
	return false;
}
bool BSphereHandler::mouseWheelEvent(const BMouseEvent& mouseEvent)
{
	BReal moveStep = this->moveStep();
	BCamera* camera = this->camera();
	if (camera->ortho())
	{
		BReal height = camera->lensHeight() * (1 + mouseEvent.wheel() * 0.1);
		BReal width = camera->lensWidth() * (1 + mouseEvent.wheel() * 0.1);
		camera->setLensWidth(width);
		camera->setLensHeight(height);
		this->fresh();
	}
	else
	{
		BVector pos = camera->position();
		BVector center = camera->center();
		BVector direct = center - pos;
		BVector move = direct * (moveStep * mouseEvent.wheel() / 100.0f);
		camera->setPosition(pos - move);
		this->fresh();
	}
	return true;
}
